#!/usr/bin/env bash
LOG_FILE="/tmp/discourse-debug.txt"
WORKING_DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )"

log() {
  if [ "$1" == "-e" ]
  then
    shift
    echo -e "$*" | tee -a "$LOG_FILE"
  else
    echo "$*" | tee -a "$LOG_FILE"
  fi
}

check_root() {
  if [[ $EUID -ne 0 ]]; then
    log "This script must be run as root. Please sudo or log in as root first." 1>&2
    exit 1
  fi
}

##
## Check whether a connection to HOSTNAME ($1) on PORT ($2) is possible
##
connect_to_port() {
  HOST="$1"
  PORT="$2"
  VERIFY=$(date +%s | sha256sum | base64 | head -c 20)
  echo -e "HTTP/1.1 200 OK\n\n $VERIFY" | nc -w 4 -l -p $PORT >/dev/null 2>&1 &
  if curl --proto =http -s $HOST:$PORT --connect-timeout 3 | grep $VERIFY >/dev/null 2>&1 &
  then
    return 0
  else
    return 1
  fi
}

check_ip_match() {
  HOST="$1"
  log
  log Checking your domain name . . .
  if connect_to_port $HOST 443
  then
    log
    log "Connection to $HOST succeeded."
  else
    log WARNING:: This server does not appear to be accessible at $HOST:443.
    log
    if connect_to_port $HOST 80
    then
	log A connection to port 80 succeeds, however.
	log This suggests that your DNS settings are correct,
	log but something is keeping traffic to port 443 from getting to your server.
	log Check your networking configuration to see that connections to port 443 are allowed.
    else
      log "A connection to http://$HOST (port 80) also fails."
      log
      log This suggests that $HOST resolves to the wrong IP address
      log or that traffic is not being routed to your server.
    fi
    log
    log Google: \"open ports YOUR CLOUD SERVICE\" for information for resolving this problem.
    log
    log This test might not work for all situations,
    log so if you can access Discourse at http://$HOST, this might not indicate a problem.
    sleep 3
  fi
}

check_docker_is_installed() {
  log -e "\n==================== DOCKER INFO ===================="
  docker_path="$(which docker.io || which docker)"
  if [ -z $docker_path ]; then
    log "Docker is not installed. Have you installed Discourse at all?"
    log "Perhaps you're looking for ./discourse-setup ."
    log "There is no point in continuing."
    exit
  else
    log -e "DOCKER VERSION: $(docker --version)"
    log -e "\nDOCKER PROCESSES (docker ps -a)\n\n$(sudo docker ps -a)\n"
  fi
}

get_OS() {
  log -e "OS: $(uname -s)"
}

check_disk_and_memory() {
  log -e "\n\n==================== MEMORY INFORMATION ===================="
  os_type=$(get_OS)
  if [ "$os_type" == "Darwin" ]; then
    log -e "RAM: $( free -m | awk '/Mem:/ {print $2}' ) \n"
  else
    log -e "RAM (MB): $( free -m --si | awk ' /Mem:/  {print $2} ')\n"
  fi
  log "$(free -m)"

  log -e "\n==================== DISK SPACE CHECK ===================="
  log "---------- OS Disk Space ----------"
  log "$(df -h / /var/discourse /var/lib/docker /var/lib/docker/* | uniq)"

  if [ "$version" != "NOT FOUND" ]
  then
    log
    log "---------- Container Disk Space ----------"
    log "$(sudo docker exec -w /var/www/discourse -i $app_name df -h / /shared/ /shared/postgres_data /shared/redis_data /shared/backups /var/log | uniq)"
  fi

  log -e "\n==================== DISK INFORMATION ===================="
  log "$( fdisk -l )"
  log -e "\n==================== END DISK INFORMATION ===================="

  free_disk="$(df /var | tail -n 1 | awk '{print $4}')"
  # Arguably ./launcher is doing this so discourse-doctor does not need to . . .
  if [ "$free_disk" -lt 5000 ]; then
  log "\n\n==================== DISK SPACE PROBLEM ===================="
    log  "WARNING: you appear to have very low disk space."
    log "This could be the cause of problems running your site."
    log "Please free up some space, or expand your disk, before continuing."
    log
    log "Run \'apt-get autoremove && apt-get autoclean\' to clean up unused"
    log "packages and \'./launcher cleanup\' to remove stale Docker containers."
    exit 1
  fi
}

get_discourse_version() {
  version=""
  version=$(wget -q --timeout=3 https://$VERSION_HOSTNAME/privacy  -O -|grep generator|head -1 |cut -d "=" -f 3|cut -d '-' -f 1 |cut -d '"' -f 2)  &> /dev/null
  if ! echo $version | grep Discourse
  then
    version=$(wget -q --timeout=3 http://$VERSION_HOSTNAME/privacy  -O -|grep generator|head -1 |cut -d "=" -f 3|cut -d '-' -f 1 |cut -d '"' -f 2) &> /dev/null
  fi
  if [ -z "$version" ]
  then
    version="NOT FOUND"
  fi
  log "Discourse version at $VERSION_HOSTNAME: $version"
}

check_if_hostname_resolves_here() {
  log "========================================"
  VERSION_HOSTNAME=$DISCOURSE_HOSTNAME
  get_discourse_version
  DISCOURSE_VERSION="$version"
  VERSION_HOSTNAME=localhost
  get_discourse_version
  LOCALHOST_VERSION="$version"
  if [ "$DISCOURSE_VERSION" != "$LOCALHOST_VERSION" ]
  then
    log "==================== DNS PROBLEM ===================="
    log "This server reports $LOCALHOST_VERSION, but $DISCOURSE_HOSTNAME reports $DISCOURSE_VERSION."
    log "This suggests that you have a DNS problem or that an intermediate proxy is to blame."
    log "If you are using Cloudflare, or a CDN, it may be improperly configured."
  fi
}

##
## get discourse configuration values from YML file
##
get_discourse_config() {
  log -e "\n==================== YML SETTINGS ===================="
  read_config "DISCOURSE_HOSTNAME"
  DISCOURSE_HOSTNAME=$read_config_result
  log DISCOURSE_HOSTNAME=$DISCOURSE_HOSTNAME
  read_config "DISCOURSE_SMTP_ADDRESS"
  SMTP_ADDRESS=$read_config_result
  log SMTP_ADDRESS=$SMTP_ADDRESS
  read_config "DISCOURSE_DEVELOPER_EMAILS"
  DEVELOPER_EMAILS=$read_config_result
  log DEVELOPER_EMAILS=$DEVELOPER_EMAILS
  read_config "DISCOURSE_SMTP_PASSWORD"
  SMTP_PASSWORD=$read_config_result
  log SMTP_PASSWORD=$read_config_result
  read_config "DISCOURSE_SMTP_PORT"
  SMTP_PORT=$read_config_result
  log SMTP_PORT=$read_config_result
  read_config "DISCOURSE_SMTP_USER_NAME"
  SMTP_USER_NAME=$read_config_result
  log SMTP_USER_NAME=$read_config_result
  read_config "LETSENCRYPT_ACCOUNT_EMAIL"
  letsencrypt_account_email=$read_config_result
  log "LETSENCRYPT_ACCOUNT_EMAIL=$letsencrypt_account_email"
}

check_plugins() {
  log -e "\n\n==================== PLUGINS ===================="
  log -e "$(grep 'git clone' containers/$app_name.yml)"
  grep git containers/$app_name.yml > /tmp/$PPID.grep

  if grep -cv "github.com/discourse" /tmp/$PPID.grep > /dev/null
  then
    log -e "\nWARNING:"
    log You have what appear to be non-official plugins.
    log "If you are having trouble, you should disable them and try rebuilding again."
  else
    log -e "\nNo non-official plugins detected."
  fi
  log -e "\nSee https://github.com/discourse/discourse/blob/master/lib/plugin/metadata.rb for the official list.\n"
}

dump_yaml() {
  log -e "\n\n==================== YML DUMP ===================="
  log Dumping $app_name.yml
  log -e "\n\n"
}

##
## read a variable from the config file and stick it in read_config_result
##
read_config() {
  config_line=$(egrep "^  #?$1:" $web_file)
  read_config_result=$(echo $config_line | awk  -F ":" '{print $2}')
  read_config_result=$(echo $read_config_result | sed "s/^\([\"']\)\(.*\)\1\$/\2/g")
}

##
## call rake emails:test inside the container
##
check_email() {
  log -e "\n==================== MAIL TEST ===================="
  log "For a robust test, get an address from http://www.mail-tester.com/"
  echo "Or just send a test message to yourself."
  EMAIL=$(echo $DEVELOPER_EMAILS |cut -d , -f 1)
  read -p "Email address for mail test? ('n' to skip) [$EMAIL]: " new_value
  if [ ! -z "$new_value" ]
  then
    EMAIL="$new_value"
  fi
  if [ "$new_value" != "n" ] && [ "$new_value" != "N" ]
  then
    log "Sending mail to $EMAIL. . . "
    log "$(sudo docker exec -w /var/www/discourse -i $app_name rake emails:test[$EMAIL])"
  else
    log "Mail test skipped."
  fi
}

get_yml_file() {
  app_name=""
  if [ -f containers/app.yml ]
  then
    app_name="app"
    web_file=containers/$app_name.yml
    log "Found $web_file"
  elif [ -f containers/web_only.yml ]
  then
    log "YML=web_only.yml"
    app_name="web_only"
    web_file=containers/$app_name.yml
    log "Found $web_file"
  else
    log "Can't find app.yml or web_only.yml."
    log "Giving up."
    exit
  fi
}

check_docker() {
  docker ps | tail -n +2 > /tmp/$UUID-docker.txt

  if grep $app_name /tmp/$UUID-docker.txt
  then
    log -e "\nDiscourse container $app_name is running"
  else
    log "==================== SERIOUS PROBLEM!!!! ===================="
    log "$app_name not running!"
    log "Attempting to rebuild"
    log "==================== REBUILD LOG ===================="
    # too hard to pass STDERR of ./launcher to log()
    ./launcher rebuild $app_name 2>&1 | tee -a $LOG_FILE
    log "==================== END REBUILD LOG ===================="
    docker ps | tail -n +2 > /tmp/$UUID-docker.txt
    if grep $app_name /tmp/$UUID-docker.txt
    then
      log -e "\nDiscourse container $app_name is now running."
      log ". . . waiting 30 seconds for container to crank up. . . "
      sleep 30
    else
      log "Failed to rebuild $app_name."
      # check_ip_match checks if curl to $DISCOURSE_HOSTNAME gets to this server
      # It works only if ports 80 and 443 are free
      check_ip_match $DISCOURSE_HOSTNAME
      log "You should probably remove any non-standard plugins and rebuild."
      NO_CONTAINER='y'
      log "Attempting to restart existing container. . . "
      ./launcher start $app_name 2>&1 | tee -a $LOG_FILE
      docker ps | tail -n +2 > /tmp/$UUID-docker.txt
      if grep $app_name /tmp/$UUID-docker.txt
      then
        log "Restarted the container."
        NO_CONTAINER='n'
      else
        log "Failed to restart the container."
      fi
    fi
  fi
}

##
## redact passwords and email addresses from log file
##
clean_up_log_file() {
  for VAR
  in SMTP_PASSWORD LETSENCRYPT_ACCOUNT_EMAIL DEVELOPER_EMAILS DISCOURSE_DB_PASSWORD 'Sending mail to'
  do
    echo "Replacing: $VAR"
    sed -i -e 's/'"$VAR"'\([=: ]\)\S*/'"$VAR"'\1REDACTED /g' $LOG_FILE
  done
}

print_done() {
  log
  log "==================== DONE! ===================="
  DOCTOR_FILE=$(date +%s | sha256sum | base64 | head -c 20).txt

  if [ $app_name == 'app' ] && [ "$NO_CONTAINER" != 'y' ]
  then
    read -p "Would you like to serve a publicly available version of this file? (Y/n)" serve
    if [ $serve == 'Y' ]
    then
        cp $LOG_FILE shared/standalone/log/var-log/$DOCTOR_FILE
        sudo docker exec -w /var/www/discourse -i $app_name cp /var/log/$DOCTOR_FILE  public
        log "The output of this program may be available at http://$DISCOURSE_HOSTNAME/$DOCTOR_FILE"
        log "You should inspect that file carefully before sharing the URL."
    else
        log "Publicly available log not generated."
    fi
  fi
  # The following is not in the web log file since it was copied above, which seems corect
  log
  log "You can examine the output of this script with "
  log "LESS=-Ri less $LOG_FILE"
  log
  log "BUT FIRST, make sure that you know the first three commands below!!!"
  log
  log "Commands to know when viewing the file with the above command (called 'less'): "
  log "q              -- quit"
  log "/error<ENTER>  -- search for the word 'error'"
  log "n              -- search for the next occurrence"
  log "g              -- go to the beginning of the file"
  log "f              -- go forward a page"
  log "b              -- go back a page"
  log "G              -- go to the end of the file"
}

initialize_log_file() {
  rm -f $LOG_FILE
  touch $LOG_FILE
  log DISCOURSE DOCTOR $(date)
  log -e "OS: $(uname -a)\n\n"
}

##
## END FUNCTION DECLARATION
##

check_root
cd $WORKING_DIR || exit
initialize_log_file
get_yml_file
get_discourse_config
check_docker_is_installed
check_docker
check_plugins
check_if_hostname_resolves_here
check_disk_and_memory
check_email
clean_up_log_file
print_done
